/*
	Copyright (c) 2008, Grant Jones <grant@podtronics.com>.
	All rights reserved.

	Redistribution and use in source and binary forms, with or
	without modification, are permitted provided that the following
	conditions are met:

		Redistributions of source code must retain the above copyright
		notice, this list of conditions and the following disclaimer.
		
		Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in
		the documentation and/or other materials provided with the
		distribution.
		
		Neither the name of the gxjones.com nor the names of its
		contributors may be used to endorse or promote products derived
		from this software without specific prior written permission.
	
	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
	FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
	COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
	OF THE POSSIBILITY OF SUCH DAMAGE.
	
	http://www.opensource.org/licenses/bsd-license.php
*/

/*
 *  edb_domain.c
 *  scatterd
 *
 *  Created by Grant Jones on 3/1/08.
 *
 */
#include "edb_main.h"

#include "edb_msg_parser.h"
#include "edb_msg_utils.h"
#include "edb_domain.h"
#include "edb_domain_index.h"
#include "strmap.h"




edb_domain_t *edb_domain_open( ProgramContextStruct *pcs, const char *dname, int dname_len )
{
	edb_domain_t *d = (edb_domain_t *)mempool_malloc_named(pcs->domains->mempool, sizeof(edb_domain_t)+dname_len, "domain reference" );
	if(!d)
		return NULL;

	cmap_init( &d->index, pcs->domains->mempool, "domain index list" );

	d->dname_sz = dname_len;
	memcpy( d->dname, dname, dname_len );

	strmap_assign(pcs->domains, dname, dname_len, d);
	
	/* initailize storage domain info */
	d->refcount = 0;
/*	
	char *safe_dname = edb_strdup(&pcs->stralloc, dname, dname_len, "domain open" );
	
	if( rtree_open( &d->rt, safe_dname, 0 ) )
		return NULL;

	printf(">> edb_domain_open \"%s\" fd: %d\n", safe_dname, d->rt.tms->fd);
*/
	
	d->rt_db_ptr = pcs->rt_db_ptr;
	
	/* gather index info */
	edb_domain_init_index( d );
	
	return d;
}

int edb_domain_close( ProgramContextStruct *pcs, edb_domain_t *d )
{
	/*return rtree_close(d->rt);*/
	return 0;
}


int edb_domain_begin( edb_domain_ref_t *dref )
{
	assert( dref->tid < 0 );
	dref->tid = tagmt_beginTransaction( dref->d->rt_db_ptr->tms );
	return dref->tid;
}

int edb_domain_end( edb_domain_ref_t *dref )
{
	int r;
	assert( dref->tid > 0 );
	r = tagmt_endTransaction(dref->d->rt_db_ptr->tms, dref->tid);
	dref->tid = -1;
	return r;
}


/*!
	@function edb_use_domain
	@abstract obtains a reference to the domain specified by dname
	@discussion Called before performing an operation on a domain
	@param pcs the program context
	@param dname the domain name
	@param dname_len the length of the domain name string
	@result a pointer which represents a reference to the domain, any
		domain operations must pass this reference; NULL on error
*/
edb_domain_ref_t *edb_domain_use( ProgramContextStruct *pcs, mempool_t *pool, const char *dname, int dname_len )
{
	
	edb_domain_ref_t *dref = (edb_domain_ref_t *)mempool_malloc_named(pool, sizeof(edb_domain_ref_t), "domain reference" );
	if(!dref)
		return NULL;

	dref->pcs = pcs;
	dref->pool = pool;
	
	
	dref->d = strmap_lookup(pcs->domains, dname, dname_len);
	
	if(!dref->d)
		dref->d  = edb_domain_open( pcs, dname, dname_len );

	if(!dref->d)
	{
		mempool_free(pool, dref);
		return NULL;
	}
	
	dref->tid = -1;
	
	dref->d->refcount ++;
	
	return dref;
}


/*!
	@function edb_release_domain
	@abstract releases a domain reference
	@discussion Releases a domain reference obtained via edb_use_domain
	@param pcs the program context
	@param dref the domain reference to release
	@result 0 on success; != 0 on error
*/
int edb_domain_release( ProgramContextStruct *pcs, edb_domain_ref_t *dref )
{
	if(!dref)
		return -1;
	if(dref->d)
	{
		dref->d->refcount --;
		//void tagm_dumpTag( TagManagerStruct *tags, FILE *out_fp, off_t start_loc, int indent )	
/*		tagm_dumpTag( dref->d->rt.tms, stdout, 0ULL, 0 );*/
	}

	mempool_free(dref->pool, dref);

	return 0;
}


void edb_domain_notify_sync( edb_domain_ref_t *dref, rtvalue_t *changed_key )
{
#define EDBSYNC_INSERT
#ifdef EDBSYNC_INSERT
	cmapStruct *r;
	mempool_t *pool = dref->pool;
	edb_net_item_t item;
	edb_net_item_attr_t attrs[2];

	if( dref->pcs->bGlobalSyncDisabled )
		return;
		
	/* make sure this isn't an insert into the edbsync domain! */
	if( dref->d->dname_sz == strlen(EDB_SYNC_DOMAIN) &&
		memcmp( dref->d->dname, EDB_SYNC_DOMAIN, dref->d->dname_sz ) == 0 )
		return;
	
	item.attr_count = 2;
	item.attrs = attrs;
	
	attrs[0].key = "affected_key";
	attrs[0].value = changed_key->data;
	attrs[0].value_sz = changed_key->size;
	
	attrs[1].key = "domain_name";
	attrs[1].value = dref->d->dname;
	attrs[1].value_sz = dref->d->dname_sz;
	
	/* TODO: generic insert/delete notifications (similar to triggers) instead of this very specific action: */
	r = edb_msg_parser( dref->pcs, pool, "INSERT INTO edbsync VALUES( affected_key:$affected_key, domain_name:$domain_name );", 1, &item );

	cmap_destroy(r);
#endif
}


int edb_domain_destruct_item( mempool_t *pool, rtvalue_t *data_in, AttrList **attrs_out )
{
	size_t cur_loc;
	AttrList *attrs_out_ptr = NULL;
	*attrs_out = NULL;
	
	for( cur_loc = 0; cur_loc < data_in->size; )
	{
		unsigned char *dout_ptr = &((unsigned char *)data_in->data)[cur_loc];
		AttrValue *attr_val = (AttrValue *)mempool_malloc_named(pool, sizeof(AttrValue), "Attribute" );
		
		
		attr_val->key.n = ((size_t *)dout_ptr)[0];
		attr_val->value.n = ((size_t *)dout_ptr)[1];

		assert( attr_val->key.n < data_in->size );
		assert( attr_val->value.n < data_in->size );
		
		attr_val->key.z = mempool_malloc_named(pool, attr_val->key.n, "Attribute Key Data" );
		memcpy( (void *)attr_val->key.z, &dout_ptr[sizeof(size_t)*2], attr_val->key.n );
		
		attr_val->value.z = mempool_malloc_named(pool, attr_val->value.n, "Attribute Value Data" );
		memcpy( (void *)attr_val->value.z, &dout_ptr[(sizeof(size_t)*2) + attr_val->key.n], attr_val->value.n );
		

//		attr_val->key.z = &dout_ptr[sizeof(size_t)*2];
//		attr_val->value.z = &dout_ptr[(sizeof(size_t)*2) + attr_val->key.n];
		
		attrs_out_ptr = edbAppendAttrListFromPool( pool, attrs_out_ptr, attr_val );
		

		cur_loc += (sizeof(size_t)*2) + attr_val->key.n + attr_val->value.n;		
	}
	
	*attrs_out = attrs_out_ptr;

	return 0;
}

int edb_domain_construct_item(  rtvalue_t *data_out, AttrList *insert_values )
{
	strmapEnumStruct se;
	char *key;
	size_t key_sz;
	AttrValue *attr_val;
	size_t cur_loc;
		
	data_out->size = 0;
	data_out->data = 0;
	
	strmap_enum( insert_values, &se );
	
	while( (attr_val = (AttrValue *)strmap_enum_next( &se, &key, &key_sz )) )
	{
#ifdef DEBUG
		edb_log_printf(0, "attr_val: ");
		edb_log_dump(0, key, key_sz );
		edb_log_printf(0, " : ");
		edb_log_dump(0, attr_val->value.z, attr_val->value.n );
		edb_log_printf(0, "\n");
#endif
		
		data_out->size += (sizeof(size_t)*2) + attr_val->key.n + attr_val->value.n;
	}
	
//	edb_log_printf(0, "payload size: %ld bytes\n", data_out->size );
	
	data_out->data = mempool_malloc_named(insert_values->mempool, data_out->size, "domain payload" );
	if(!data_out->data)
		return -1;

	strmap_enum( insert_values, &se );
	cur_loc = 0;
	while( (attr_val = (AttrValue *)strmap_enum_next( &se, &key, &key_sz )) )
	{
		char *dout_ptr = &((char *)data_out->data)[cur_loc];
		
		((size_t *)dout_ptr)[0] = attr_val->key.n;
		((size_t *)dout_ptr)[1] = attr_val->value.n;
		
		memcpy( &dout_ptr[sizeof(size_t)*2], attr_val->key.z, attr_val->key.n );
		memcpy( &dout_ptr[(sizeof(size_t)*2) + attr_val->key.n], attr_val->value.z, attr_val->value.n );
		cur_loc += (sizeof(size_t)*2) + attr_val->key.n + attr_val->value.n;
	}	
	return 0;
}




